package crypto

import (
    "fmt"
    "math/big"
)

// BLS represents BLS signature scheme (can use BLS12-381 or secp256k1)
type BLS struct { 
    g1 G1
    g2 G2
    useSecp256k1 bool
    secpAdapter *Secp256k1Adapter
}

// NewBLS creates a new BLS instance
func NewBLS() *BLS { 
    bls := &BLS{ 
        g1: GetG1(), 
        g2: GetG2(),
        useSecp256k1: IsSecp256k1(),
    }
    if bls.useSecp256k1 {
        bls.secpAdapter = NewSecp256k1Adapter()
    }
    return bls
}

// HashMessage hashes a message to a G2 element
func (bls *BLS) HashMessage(message interface{}) *G2 {
	if bls.useSecp256k1 {
		// For secp256k1, return a dummy G2 element
		// The actual hashing will be done in the signing process
		return &G2{}
	}
	
	// Convert message to bytes
	var data []byte
	switch m := message.(type) {
	case []byte:
		data = m
	case string:
		data = []byte(m)
	default:
		data = []byte(fmt.Sprintf("%v", m))
	}
	
	// Hash to G2
    g := HashToG2(data)
    return &g
}

// Sign signs a message with a private key
func (bls *BLS) Sign(sk *ZR, message interface{}) *G2 {
	if bls.useSecp256k1 {
		// For secp256k1, use the adapter
		// Convert ZR to AdapterZR (this is a simplified conversion)
		adapterSK := &AdapterZR{&Secp256k1Scalar{sk.Element.BigInt(new(big.Int))}}
		_ = bls.secpAdapter.Sign(adapterSK, message)
		// Convert back to G2 (simplified)
		return &G2{}
	}
	
	// Hash message to G2
	h := bls.HashMessage(message)
	
	// Compute signature: sig = h^sk
    sig := MulG2(h, sk)
    return &sig
}

// VerifyShare verifies a signature share
func (bls *BLS) VerifyShare(pk *G1, sig *G2, message interface{}) bool {
	// Hash message to G2
	h := bls.HashMessage(message)
	
	// Check: e(g1, sig) == e(pk, h)
    left := Pair(&bls.g1, sig)
    right := Pair(pk, h)
    return left.Equal(&right)
}

// CombineShares combines signature shares using Lagrange interpolation
func (bls *BLS) CombineShares(sigs map[int]*G2, indices []int) *G2 {
	if len(sigs) == 0 {
		return nil
	}
	
	// Convert indices to set for Lagrange computation
	indicesSet := make(map[int]bool)
	for _, idx := range indices {
		indicesSet[idx] = true
	}
	
	var result G2
	result.SetInfinity()
	
	// Combine shares using Lagrange coefficients
	for j, sig := range sigs {
		if !indicesSet[j] {
			continue
		}
		
		// Compute Lagrange coefficient
		coeff := bls.lagrangeCoefficient(indices, j)
		
		// Compute sig^coeff
    poweredSig := MulG2(sig, &coeff)
    result = AddG2(&result, &poweredSig)
	}
	
	return &result
}

// VerifySignature verifies a complete signature
func (bls *BLS) VerifySignature(pk *G1, sig *G2, message interface{}) bool {
	// Hash message to G2
	h := bls.HashMessage(message)
	
	// Check: e(g1, sig) == e(pk, h)
    left := Pair(&bls.g1, sig)
    right := Pair(pk, h)
    return left.Equal(&right)
}

// GenerateThresholdKeys generates threshold signature keys
func (bls *BLS) GenerateThresholdKeys(n, t int) (*G1, []G1, []ZR) {
	// Generate random polynomial of degree t-1
	coeffs := make([]ZR, t)
	for i := 0; i < t; i++ {
		coeffs[i].SetRandom()
	}
	
	// Compute public keys
    pks := make([]G1, n)
	for i := 0; i < n; i++ {
		// Evaluate polynomial at i+1
		share := bls.evalPolynomial(coeffs, int64(i+1))
        pks[i] = MulG1(&bls.g1, &share)
	}
	
	// Compute threshold public key (polynomial evaluated at 0)
    thresholdPK := bls.evalPolynomial(coeffs, 0)
    thresholdPKG1 := MulG1(&bls.g1, &thresholdPK)
	
	// Compute private key shares
	sks := make([]ZR, n)
	for i := 0; i < n; i++ {
		sks[i] = bls.evalPolynomial(coeffs, int64(i+1))
	}
	
	return &thresholdPKG1, pks, sks
}

// ThresholdSign signs a message with threshold signature
func (bls *BLS) ThresholdSign(sks []ZR, message interface{}, signers []int) *G2 {
	if len(sks) != len(signers) {
		panic("number of private keys must match number of signers")
	}
	
	// Generate signature shares
	sigShares := make(map[int]*G2)
	for i, sk := range sks {
		sig := bls.Sign(&sk, message)
		sigShares[signers[i]] = sig
	}
	
	// Combine shares
	return bls.CombineShares(sigShares, signers)
}

// ThresholdVerify verifies a threshold signature
func (bls *BLS) ThresholdVerify(thresholdPK *G1, sig *G2, message interface{}) bool {
	return bls.VerifySignature(thresholdPK, sig, message)
}

// Helper functions

// lagrangeCoefficient computes Lagrange coefficient for interpolation
func (bls *BLS) lagrangeCoefficient(indices []int, j int) ZR {
	var numerator ZR
	numerator.SetOne()
	var denominator ZR
	denominator.SetOne()
	
	for _, idx := range indices {
		if idx == j {
			continue
		}
		
		// numerator *= (0 - idx)
		var term ZR
		term.SetInt64(int64(-idx))
		numerator.Mul(&numerator, &term)
		
		// denominator *= (j - idx)
		var denomTerm ZR
		denomTerm.SetInt64(int64(j - idx))
		denominator.Mul(&denominator, &denomTerm)
	}
	
	// Return numerator / denominator
	var result ZR
	result.Div(&numerator, &denominator)
	return result
}

// evalPolynomial evaluates a polynomial at a given point
func (bls *BLS) evalPolynomial(coeffs []ZR, x int64) ZR {
	var result ZR
	result.SetZero()
	
	xZR := ZRFromInt(x)
	var xPower ZR
	xPower.SetOne()
	
	for i := 0; i < len(coeffs); i++ {
		var term ZR
		term.Mul(&coeffs[i], &xPower)
		result.Add(&result, &term)
		
		if i < len(coeffs)-1 {
			var temp ZR
			temp.Mul(&xPower, &xZR)
			xPower = temp
		}
	}
	
	return result
}

// Global BLS instance
var DefaultBLS = NewBLS()

// Convenience functions using the global instance

// HashMessage hashes a message using the default BLS instance
func HashMessage(message interface{}) *G2 {
	return DefaultBLS.HashMessage(message)
}

// Sign signs a message using the default BLS instance
func Sign(sk *ZR, message interface{}) *G2 {
	return DefaultBLS.Sign(sk, message)
}

// VerifyShare verifies a signature share using the default BLS instance
func VerifyShare(pk *G1, sig *G2, message interface{}) bool {
	return DefaultBLS.VerifyShare(pk, sig, message)
}

// CombineShares combines signature shares using the default BLS instance
func CombineShares(sigs map[int]*G2, indices []int) *G2 {
	return DefaultBLS.CombineShares(sigs, indices)
}

// VerifySignature verifies a signature using the default BLS instance
func VerifySignature(pk *G1, sig *G2, message interface{}) bool {
	return DefaultBLS.VerifySignature(pk, sig, message)
}

// GenerateThresholdKeys generates threshold keys using the default BLS instance
func GenerateThresholdKeys(n, t int) (*G1, []G1, []ZR) {
	return DefaultBLS.GenerateThresholdKeys(n, t)
}

// ThresholdSign signs with threshold signature using the default BLS instance
func ThresholdSign(sks []ZR, message interface{}, signers []int) *G2 {
	return DefaultBLS.ThresholdSign(sks, message, signers)
}

// ThresholdVerify verifies threshold signature using the default BLS instance
func ThresholdVerify(thresholdPK *G1, sig *G2, message interface{}) bool {
	return DefaultBLS.ThresholdVerify(thresholdPK, sig, message)
}
